import Callout from 'components/Callout';
import PartnerContentLink from 'components/PartnerContentLink';

# Rendering i18n messages

The main part of handling internationalization (typically referred to as _i18n_) in your Next.js app is to provide messages based on the language of the user.

## Terminology

- **Locale**: We use this term to describe an identifier that contains the language and formatting preferences of users. Apart from the language, a locale can include optional regional information (e.g. `en-US`). Locales are specified as [IETF BCP 47 language tags](https://en.wikipedia.org/wiki/IETF_language_tag).
- **Messages**: These are collections of namespace-label pairs that are grouped by locale (e.g. `en-US.json`).

## Structuring messages

To group your messages within a locale, it's recommended to use component names as namespaces and embrace them as the primary unit of code organization in your app. You can of course also use a different structure, depending on what suits your app best.

```json filename="en.json"
{
  "About": {
    "title": "About us"
  }
}
```

You can render messages from within a React component via the `useTranslations` hook:

```tsx filename="About.tsx"
import {useTranslations} from 'next-intl';

function About() {
  const t = useTranslations('About');
  return <h1>{t('title')}</h1>;
}
```

To retrieve all available messages in a component, you can omit the namespace path:

```js
const t = useTranslations();

t('About.title');
```

<details>
<summary>How can I provide more structure for messages?</summary>

Optionally, you can structure your messages as nested objects.

```json filename="en.json"
{
  "auth": {
    "SignUp": {
      "title": "Sign up",
      "form": {
        "placeholder": "Please enter your name",
        "submit": "Submit"
      }
    }
  }
}
```

```tsx filename="SignUp.tsx"
import {useTranslations} from 'next-intl';

function SignUp() {
  // Provide the lowest common denominator that contains
  // all messages this component needs to consume.
  const t = useTranslations('auth.SignUp');

  return (
    <>
      <h1>{t('title')}</h1>
      <form>
        <input
          // The remaining hierarchy can be resolved by
          // using `.` to access nested messages.
          placeholder={t('form.placeholder')}
        />
        <button type="submit">{t('form.submit')}</button>
      </form>
    </>
  );
}
```

</details>

<details>
<summary>How can I reuse messages?</summary>

As your app grows, you'll want to reuse messages among your components. If you use component names as namespaces to structure your messages, you'll automatically benefit from reusable messages by reusing your components.

**Examples:**

- You're displaying products in your app and often need to resolve a category identifier to a human readable label (e.g. `new` → "Just in"). To ensure consistency, you can add a `ProductCategory` component that turns the `category` into a styled badge.
- You commonly need a dialog that displays a "confirm" and "cancel" button. In this case, consider adding a `ConfirmDialog` component to reuse the messages along with the functionality.

There might be cases where you want to use the same message in different components, but there's no reasonable opportunity for sharing the message via a component. This might be symptom that these components should use separate messages, even if they happen to be the same for the time being. By using separate labels, you gain the flexibility to use more specific labels (e.g. "not now" instead of "cancel").

For edge cases where the reuse of messages among different components is hard to achieve with shared components, you can either use messages from different namespaces via `useTranslations()`, or you can consider adding a hook that shares the translation behavior:

```tsx filename="useLocaleLabel.tsx"
export default function useLocaleLabel() {
  const t = useLocaleLabel('useLocaleLabel');

  function getLocaleLabel(locale: 'en' | 'de') {
    return t('label', {locale});
  }

  return getLocaleLabel;
}
```

```json
{
  "useLocaleLabel": {
    "label": "{locale, select, en {English} de {German} other {Unknown}}"
  }
}
```

</details>

<details>
<summary>How can I use translations outside of components?</summary>

`next-intl` is heavily based on the `useTranslations` API which is intended to consume translations from within React components. This may seem like a limitation, but this is intentional and aims to encourage the use of proven patterns that avoid potential issues—especially if they are easy to overlook.

If you're interested to dive deeper into this topic, there's a blog post that discusses the background of this design decision: [How (not) to use translations outside of React components](/blog/translations-outside-of-react-components).

There's one exception however: [Using `next-intl` with the Next.js Metadata API and Route Handlers](/docs/environments/metadata-route-handlers).

</details>

## Rendering ICU messages

`next-intl` uses [ICU message syntax](https://unicode-org.github.io/icu/userguide/format_parse/messages/) that allows you to express language nuances and separates state handling within messages from your app code.

<Callout>

To work with ICU messages, it can be helpful to use an editor that supports this syntax. E.g. the <PartnerContentLink name="messages-rendering" href="https://support.crowdin.com/icu-message-syntax/">Crowdin Editor</PartnerContentLink> can be used by translators to work on translations without having to change app code.

</Callout>

### Static messages

Static messages will be used as-is:

```json filename="en.json"
"message": "Hello world!"
```

```js
t('message'); // "Hello world!"
```

### Interpolation of dynamic values

Dynamic values can be inserted into messages by using curly braces:

```json filename="en.json"
"message": "Hello {name}!"
```

```js
t('message', {name: 'Jane'}); // "Hello Jane!"
```

### Cardinal pluralization

To express the pluralization of a given number of items, the `plural` argument can be used:

```json filename="en.json"
"message": "You have {count, plural, =0 {no followers yet} =1 {one follower} other {# followers}}."
```

```js
t('message', {count: 3580}); // "You have 3,580 followers."
```

Note that by using the `#` marker, the value will be [formatted as a number](/docs/usage/numbers).

<details>
<summary>Which plural forms are supported?</summary>

Depending on the language, different forms of cardinal pluralization are supported.

For example, English has two forms: One for the singular (e.g. "1 _follower_") and one for everything else (e.g. "0 _followers_" and "2 _followers_"). In contrast, Chinese only has one form, while Arabic has six.

`next-intl` uses [`Intl.PluralRules`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Intl/PluralRules) to detect the correct _tag_ that should be used for a given number.

**These tags are supported:**

- `zero`
- `one`
- `two`
- `few`
- `many`
- `other`

The exact range that `few` and `many` applies to varies depending on the locale (see [the language plural rules table in the Unicode CLDR](https://www.unicode.org/cldr/charts/43/supplemental/language_plural_rules.html)).

To match a specific number, `next-intl` additionally supports the special `=value` syntax (e.g. `=3`) that always takes precedence.

</details>

### Ordinal pluralization

To apply pluralization based on an order of items, the `selectordinal` argument can be used:

```tsx filename="en.json"
"message": "It's your {year, selectordinal, one {#st} two {#nd} few {#rd} other {#th}} birthday!"
```

<details>
<summary>Which plural forms are supported?</summary>

Depending on the language, different forms of ordinal pluralization are supported.

For example, English has four forms: "th", "st", "nd" and "rd" (e.g. 1st, 2nd, 3rd, 4th … 21st, 22nd, 23rd, 24th, etc.). In contrast, both Chinese and Arabic use only one form for ordinal numbers.

`next-intl` uses [`Intl.PluralRules`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Intl/PluralRules) to detect the correct _tag_ that should be used for a given number.

**These tags are supported:**

- `zero`
- `one`
- `two`
- `few`
- `many`
- `other`

The exact range that `few` and `many` applies to varies depending on the locale (see [the language plural rules table in the Unicode CLDR](https://www.unicode.org/cldr/charts/43/supplemental/language_plural_rules.html)).

To match a specific number, `next-intl` additionally supports the special `=value` syntax (e.g. `=3`) that always takes precedence.

</details>

### Selecting enum-based values

To map identifiers to human readable labels, you can use the `select` argument:

```tsx filename="en.json"
"message": "{gender, select, female {She} male {He} other {They}} is online."
```

```js
t('message', {gender: 'female'}); // "She is online."
```

**Note**: The `other` case is required and will be used when none of the specific values match.

### Escaping

Since curly braces are used for [interpolating dynamic values](/docs/usage/messages#interpolation-of-dynamic-values), you can escape them with the `'` marker to use the actual symbol in messages:

```json filename="en.json"
"message": "Escape curly braces with single quotes (e.g. '{name'})"
```

```js
t('message'); // "Escape curly braces with single quotes (e.g. {name})"
```

## Rich text

You can format rich text with custom tags and map them to React components:

```json filename="en.json"
{
  "message": "Please refer to <guidelines>the guidelines</guidelines>."
}
```

```js
// Returns `<>Please refer to <a href="/guidelines">the guidelines</a>.</>`
t.rich('message', {
  guidelines: (chunks) => <a href="/guidelines">{chunks}</a>
});
```

Tags can be arbitrarily nested (e.g. `This is <important><very>very</very> important</important>`).

<details>
<summary>How can I reuse a tag across my app?</summary>

If you want to use the same tag across your app, you can configure it via [default translation values](/docs/configuration#default-translation-values).

</details>

<details>
<summary>How can I use "self-closing" tags without any chunks?</summary>

Messages can use tags without any chunks as children, but syntax-wise, a closing tag is required by the ICU parser:

```json filename="en.json"
{
  "message": "Hello,<br></br>how are you?"
}
```

```js
t.rich('message', {
  br: () => <br />
});
```

</details>

<details>
<summary>How can I pass attributes to tags?</summary>

Attributes can only be set on the call site, not within messages:

```json filename="en.json"
{
  "message": "Go to <profile>my profile</profile>"
}
```

```js
t.rich('message', {
  profile: (chunks) => <Link href="/profile">{chunks}</Link>
});
```

For the use case of localizing pathnames, consider using [`createLocalizedPathnamesNavigation`](/docs/routing/navigation#localized-pathnames).

In case you have attribute values that are required to be configured as part of your messages, you can retrieve them from a separate message and pass them to another one as an attribute:

```json filename="en.json"
{
  "message": "See this <partner>partner website</partner>.",
  "partnerHref": "https://partner.example.com"
}
```

```js
t.rich('message', {
  partner: (chunks) => <a href={t('partnerHref')}>{chunks}</a>
});
```

</details>

## HTML markup

To render rich text, you typically want to use [rich text formatting](#rich-text). However, if you have a use case where you need to emit raw HTML markup, you can use the `t.markup` function:

```json filename="en.json"
{
  "markup": "This is <important>important</important>"
}
```

```js
// Returns 'This is <b>important</b>'
t.markup('markup', {
  important: (chunks) => `<b>${chunks}</b>`
});
```

Note that unlike `t.rich`, the provided markup functions accept `chunks` as a `string` and also return a `string` where the `chunks` are wrapped accordingly.

## Raw messages

Messages are always parsed and therefore e.g. for rich text formatting you need to supply the necessary tags. If you want to avoid the parsing, e.g. because you have raw HTML stored in a message, there's a separate API for this use case:

```json filename="en.json"
{
  "content": "<h1>Headline<h1><p>This is raw HTML</p>"
}
```

```js
<div dangerouslySetInnerHTML={{__html: t.raw('content')}} />
```

<Callout type="warning">
  **Important**: You should always sanitize the content that you pass to
  [`dangerouslySetInnerHTML`](https://react.dev/reference/react-dom/components/common#dangerously-setting-the-inner-html)
  to avoid cross-site scripting attacks.
</Callout>

The value of a raw message can be any valid JSON value: strings, booleans, objects and arrays.

## Arrays of messages

If you need to render a list of messages, the recommended approach is to map an array of keys to the corresponding messages:

```json filename="en.json"
{
  "CompanyStats": {
    "yearsOfService": {
      "title": "Years of service",
      "value": "34"
    },
    "happyClients": {
      "title": "Happy clients",
      "value": "1.000+"
    },
    "partners": {
      "title": "Products",
      "value": "5.000+"
    }
  }
}
```

```tsx filename="CompanyStats.tsx"
import {useTranslations} from 'next-intl';

function CompanyStats() {
  const t = useTranslations('CompanyStats');
  const keys = ['yearsOfService', 'happyClients', 'partners'] as const;

  return (
    <ul>
      {keys.map((key) => (
        <li key={key}>
          <h2>{t(`${key}.title`)}</h2>
          <p>{t(`${key}.value`)}</p>
        </li>
      ))}
    </ul>
  );
}
```

<details>
<summary>Why can't I just use arrays in my messages?</summary>

The advantage of this approach over supporting arrays in messages is that this way you can use the formatting capabilities, e.g. to interpolate values into individual messages.

```json filename="en.json"
{
  "Benefits": {
    "zero-config": "Works with <b>zero config</b>",
    "customizable": "Easy to <b>customize</b>",
    "fast": "Blazingly <b>fast</b>"
  }
}
```

```tsx filename="Benefits.tsx"
import {useTranslations} from 'next-intl';

function Benefits() {
  const t = useTranslations('Benefits');
  return (
    <ul>
      {['zero-config', 'customizable', 'fast'].map((key, index, arr) => {
        const color = `hsl(${(index / (arr.length - 1)) * 360}deg 70% 50%)`;
        return (
          <li key={key}>
            {t(key, {b: (chunks) => <b style={{color}}>{chunks}</b>})}
          </li>
        );
      })}
    </ul>
  );
}
```

</details>

## Right-to-left languages

Languages such as Arabic, Hebrew and Persian use [right-to-left script](https://en.wikipedia.org/wiki/Right-to-left_script) (often abbreviated as RTL). For these languages, writing begins on the right side of the page and continues to the left.

**Example:**

<div dir="rtl" className="text-right mt-6">

```
النص في اللغة العربية _مثلا_ يُقرأ من اليمين لليسار
```

</div>

In addition to providing translated messages, proper RTL localization requires:

1. Providing [the `dir` attribute](https://developer.mozilla.org/en-US/docs/Web/HTML/Global_attributes/dir) on the document
2. Layout mirroring, e.g. by using [CSS logical properties](https://developer.mozilla.org/en-US/docs/Web/CSS/CSS_logical_properties_and_values)
3. Element mirroring, e.g. by customizing icons

To handle these cases in your components, you can set up a reusable hook:

```tsx filename="useTextDirection.tsx"
import {isRtlLang} from 'rtl-detect';
import {useLocale} from 'next-intl';

export default function useTextDirection(locale) {
  const defaultLocale = useLocale();
  if (!locale) locale = defaultLocale;
  return isRtlLang(locale) ? 'rtl' : 'ltr';
}
```

… and use it accordingly:

```tsx filename="app/[locale]/layout.tsx"
import useTextDirection from './useTextDirection';

export default function Layout({children, params: {locale}}) {
  const direction = useTextDirection(locale);

  return (
    <html lang={locale} dir={direction}>
      {/* ... */}
    </html>
  );
}
```

```tsx filename="components/Breadcrumbs.tsx"
import {useTranslations} from 'next-intl';
import useTextDirection from './useTextDirection';

export default function Breadcrumbs({children, params}) {
  const t = useTranslations('Breadcrumbs');
  const direction = useTextDirection();

  return (
    <div style={{display: 'flex'}}>
      <p>{t('home')}</p>
      <div style={{marginInlineStart: 10}}>
        {direction === 'ltr' ? <ArrowRight /> : <ArrowLeft />}
      </div>
      <p style={{marginInlineStart: 10}}>{t('about')}</p>
    </div>
  );
}
```
